#define CHECK_NUMBER

using System;

namespace FixedPointMath
{
    [Serializable]
    public struct Number : IEquatable<Number>, IComparable<Number>
    {
        /// <summary>
        ///数值精度: 1/(1024*1024)
        /// </summary>
        public const int BitOffset = 20;

        public static Number Zero = new Number { value = 0L };
        public static Number One = new Number { value = R2N };

        public static Number N0 = new Number { value = 0L };
        public static Number N1 = new Number { value = R2N };
        public static Number N2 = new Number { value = 2L << BitOffset };
        public static Number N4 = new Number { value = 4L << BitOffset };
        public static Number N10 = new Number { value = 10L << BitOffset };
        public static Number N90 = new Number { value = 90L << BitOffset };
        public static Number N100 = new Number { value = 100L << BitOffset };
        public static Number N180 = new Number { value = 180L << BitOffset };
        public static Number N360 = new Number { value = 360L << BitOffset };
        public static Number N1000 = new Number { value = 1000L << BitOffset };
        public static Number N9000 = new Number { value = 9000L << BitOffset };
        public static Number N18000 = new Number { value = 18000L << BitOffset };
        public static Number N1000000 = new Number { value = 1000000L << BitOffset };

        public static Number NN1 = new Number { value = -1L << BitOffset };

        public const long R2 = 2L;
        public const long R4 = 4L;
        public const long R10 = 10L;
        public const long R100 = 100L;
        public const long R180 = 180L;
        public const long R1000 = 1000L;
        public const long R10000 = 10000L;
        public const long R1000000 = 1000000L;
        public const long RMaxRealValue = long.MaxValue >> BitOffset;
        public const long RMinRealValue = long.MinValue >> BitOffset;

        /// <summary>
        /// 存储时，乘以此数值
        /// </summary>
        private const long R2N = 1L << BitOffset;

        public static Number MaxValue = new Number { value = RMaxRealValue };
        public static Number MinValue = new Number { value = RMinRealValue };
        public static Number Epsilon = new Number { value = 1 };

        public static Number OneOf2 = N1 / R2;
        public static Number OneOf10 = N1 / R10;
        public static Number OneOf100 = N1 / R100;
        public static Number OneOf1000 = N1 / R1000;
        public static Number OneOf1000000 = N1 / R1000000;

        public static Number PI = Math.PI;
        public static Number HalfPI = PI / Number.R2;
        public static Number QuarterPI = PI / Number.R4;
        public static Number DoublePI = Math.PI * Number.R2;

        /// <summary>
        /// 存储值
        /// </summary>
        public long value;

        #region implement
        public override bool Equals(object o)
        {
            if (o is Number)
            {
                var oth = (Number)o;
                return this.value == oth.value;
            }
            return false;
        }
        public override string ToString()
        {
            return ((float)this).ToString();
        }
        public override int GetHashCode()
        {
            return this.value.GetHashCode();
        }

        /// <summary>
        /// IEquatable
        /// </summary>
        /// <param name="oth"></param>
        /// <returns></returns>
        public bool Equals(Number oth)
        {
            return this.value == oth.value;
        }

        /// <summary>
        /// IComparable
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo(Number other)
        {
            return this.value.CompareTo(other.value);
        }
        #endregion

        #region 数值转换
        public static implicit operator Number(float v)
        {
#if CHECK_NUMBER
            {
                if ((long)v > RMaxRealValue || (long)v < RMinRealValue)
                {
                    throw new Exception("Number public static explicit operator Number(float v) 将溢出");
                }
            }
#endif

            Number number;
            number.value = (long)(v * R2N);
            return number;
        }
        public static explicit operator float(Number number)
        {
            return number.value / (float)R2N;
        }

        public static implicit operator Number(double v)
        {
#if CHECK_NUMBER
            {
                if ((long)v > RMaxRealValue || (long)v < RMinRealValue)
                {
                    throw new Exception("Number public static explicit operator Number(float v) 将溢出");
                }
            }
#endif

            Number number;
            number.value = (long)(v * R2N);

#if CHECK_NUMBER
            {
                if (v > 0 && number.value < v || v < 0 && number.value > v)
                {
                    throw new Exception("Number public static explicit operator Number(float v) 将溢出");
                }
            }
#endif

            return number;
        }
        public static explicit operator double(Number number)
        {
            return number.value / (double)R2N;
        }

        public static implicit operator Number(int v)
        {
            Number number;
            number.value = (long)v << BitOffset;
            return number;
        }
        public static explicit operator int(Number number)
        {
            return (int)(number.value >> BitOffset);
        }
        
        public static implicit operator Number(long v)
        {
#if CHECK_NUMBER
            {
                if (v > RMaxRealValue || v < RMinRealValue)
                {
                    throw new Exception("Number public static implicit operator Number(long v) 将溢出");
                }
            }
#endif

            Number number;
            number.value = v << BitOffset;
            return number;
        }
        public static explicit operator long(Number number)
        {
            return number.value >> BitOffset;
        }
        #endregion

        #region 基本运算及比较
        public static Number operator -(Number a)
        {
#if CHECK_NUMBER
            {
                if (a.value == long.MinValue)
                {
                    throw new Exception("Number public static Number operator -(Number a) 将溢出");
                }
            }
#endif

            Number number;
            number.value = -a.value;

            return number;
        }

        public static Number operator +(Number a, Number b)
        {
            Number number;
            number.value = a.value + b.value;

#if CHECK_NUMBER
            {
                if (a.value > 0 && b.value > 0 && number.value < 0 || a.value < 0 && b.value < 0 && number.value > 0)
                {
                    throw new Exception("Number public static Number operator +(Number a, Number b) 将溢出");
                }
            }
#endif

            return number;
        }

        public static Number operator +(Number a, int b)
        {
            Number number;
            number.value = a.value + ((long)b << BitOffset);

#if CHECK_NUMBER
            {
                if (a.value > 0 && b > 0 && number.value < 0 || a.value < 0 && b < 0 && number.value > 0)
                {
                    throw new Exception("Number public static Number operator +(Number a, int b) 将溢出");
                }
            }
#endif

            return number;
        }

        public static Number operator +(Number a, long b)
        {
#if CHECK_NUMBER
            {
                if (b > RMaxRealValue || b < RMinRealValue)
                {
                    throw new Exception("Number public static Number operator +(Number a, long b) 将溢出");
                }
            }
#endif

            Number number;
            number.value = a.value + (b << BitOffset);

#if CHECK_NUMBER
            {
                if (a.value > 0 && b > 0 && number.value < 0 || a.value < 0 && b < 0 && number.value > 0)
                {
                    throw new Exception("Number public static Number operator +(Number a, Number b) 将溢出");
                }
            }
#endif

            return number;
        }

        public static Number operator -(Number a, Number b)
        {
            Number number;
            number.value = a.value - b.value;

#if CHECK_NUMBER
            {
                if (b.value != long.MinValue)
                {
                    if (a.value > 0 && b.value < 0 && number.value < 0 || a.value < 0 && b.value > 0 && number.value > 0)
                    {
                        throw new Exception("Number public static Number operator -(Number a, Number b) 将溢出");
                    }
                }
                else
                {
                    if (a.value >= 0)
                    {
                        throw new Exception("Number public static Number operator -(Number a, Number b) 将溢出");
                    }
                }
            }
#endif

            return number;
        }

        public static Number operator -(Number a, int b)
        {
            Number number;
            number.value = a.value - ((long)b << BitOffset);

#if CHECK_NUMBER
            {
                if (a.value > 0 && b < 0 && number.value < 0 || a.value < 0 && b > 0 && number.value > 0)
                {
                    throw new Exception("Number public static Number operator -(Number a, int b) 将溢出");
                }
            }
#endif

            return number;
        }

        public static Number operator -(Number a, long b)
        {
#if CHECK_NUMBER
            {
                if (b > RMaxRealValue || b < RMinRealValue)
                {
                    throw new Exception("Number public static Number operator -(Number a, long b) 将溢出");
                }
            }
#endif

            Number number;
            number.value = a.value - (b << BitOffset);

#if CHECK_NUMBER
            {
                if (a.value > 0 && b < 0 && number.value < 0 || a.value < 0 && b > 0 && number.value > 0)
                {
                    throw new Exception("Number public static Number operator -(Number a, Number b) 将溢出");
                }
            }
#endif

            return number;
        }

        public static Number operator *(Number a, Number b)
        {
            Number number;
            number.value = a.value * b.value;

#if CHECK_NUMBER
            {
                if (a.value != 0 && number.value / a.value != b.value)
                {
                    throw new Exception("Number public static Number operator *(Number a, Number b) 将溢出");
                }
            }
#endif
            number.value = number.value >> BitOffset;

            return number;
        }

        public static Number operator *(Number a, int b)
        {
            Number number;
            number.value = a.value * b;

#if CHECK_NUMBER
            {
                if (a.value != 0 && number.value / a.value != b)
                {
                    throw new Exception("Number public static Number operator *(Number a, int b) 将溢出");
                }
            }
#endif

            return number;
        }

        public static Number operator *(Number a, long b)
        {
            Number number;
            number.value = a.value * b;

#if CHECK_NUMBER
            {
                if (a.value != 0 && number.value / a.value != b)
                {
                    throw new Exception("Number public static Number operator *(Number a, long b) 将溢出");
                }
            }
#endif

            return number;
        }

        public static Number operator *(int a, Number b)
        {
            Number number;
            number.value = b.value * a;

#if CHECK_NUMBER
            {
                if (b.value != 0 && number.value / b.value != a)
                {
                    throw new Exception("Number public static Number operator *(int a, Number b) 将溢出");
                }
            }
#endif

            return number;
        }

        public static Number operator *(long a, Number b)
        {
            Number number;
            number.value = b.value * a;

#if CHECK_NUMBER
            {
                if (b.value != 0 && number.value / b.value != a)
                {
                    throw new Exception("Number public static Number operator *(long a, Number b) 将溢出");
                }
            }
#endif

            return number;
        }

        public static Number operator /(Number a, Number b)
        {
#if CHECK_NUMBER
            {
                if (b.value == 0)
                {
                    throw new Exception("Number public static Number operator /(Number a, Number b) 将除0溢出");
                }
                else if (a.value > RMaxRealValue || a.value < RMinRealValue)
                {
                    throw new Exception("Number public static Number operator /(Number a, Number b) 将溢出");
                }
            }
#endif

            Number number;
            number.value = (a.value << BitOffset) / b.value;

            return number;
        }

        public static Number operator /(Number a, int b)
        {
#if CHECK_NUMBER
            {
                if (b == 0)
                {
                    throw new Exception("Number public static Number operator /(Number a, int b) 将除0溢出");
                }
            }
#endif

            Number number;
            number.value = a.value / b;

            return number;
        }

        public static Number operator /(Number a, long b)
        {
#if CHECK_NUMBER
            {
                if (b == 0)
                {
                    throw new Exception("Number public static Number operator /(Number a, long b) 将除0溢出");
                }
            }
#endif

            Number number;
            number.value = a.value / b;

            return number;
        }

        public static Number operator %(Number a, Number b)
        {
#if CHECK_NUMBER
            {
                if (b.value == 0)
                {
                    throw new Exception("Number public static Number operator /(Number a, Number b) 无效");
                }
            }
#endif

            Number number;
            number.value = a.value % b.value;

            return number;
        }

        public static Number operator %(Number a, int b)
        {
#if CHECK_NUMBER
            {
                if (b == 0)
                {
                    throw new Exception("Number public static Number operator %(Number a, int b) 无效");
                }
            }
#endif

            Number number;
            number.value = a.value % ((long)b << BitOffset);

            return number;
        }

        public static Number operator %(Number a, long b)
        {
#if CHECK_NUMBER
            {
                if (b == 0)
                {
                    throw new Exception("Number public static Number operator %(Number a, long b) 无效");
                }
            }
#endif

            Number number;
            number.value = a.value % (b << BitOffset);

            return number;
        }

        public static bool operator >(Number a, Number b)
        {
            return a.value > b.value;
        }

        public static bool operator <(Number a, Number b)
        {
            return a.value < b.value;
        }

        public static bool operator >=(Number a, Number b)
        {
            return a.value >= b.value;
        }

        public static bool operator <=(Number a, Number b)
        {
            return a.value <= b.value;
        }

        public static bool operator ==(Number a, Number b)
        {
            return a.value == b.value;
        }

        public static bool operator !=(Number a, Number b)
        {
            return a.value != b.value;
        }

        public static bool operator ==(Number a, int b)
        {
            return a.value == ((long)b << BitOffset);
        }

        public static bool operator !=(Number a, int b)
        {
            return a.value != ((long)b << BitOffset);
        }

        public static bool operator ==(int a, Number b)
        {
            return b.value == ((long)a << BitOffset);
        }

        public static bool operator !=(int a, Number b)
        {
            return b.value != ((long)a << BitOffset);
        }

        public static bool operator ==(Number a, long b)
        {
#if CHECK_NUMBER
            {
                if (b > RMaxRealValue || b < RMinRealValue)
                {
                    throw new Exception("Number public static bool operator ==(Number a, long b) 将溢出");
                }
            }
#endif

            return a.value == (b << BitOffset);
        }

        public static bool operator !=(Number a, long b)
        {
#if CHECK_NUMBER
            {
                if (b > RMaxRealValue || b < RMinRealValue)
                {
                    throw new Exception("Number public static bool operator !=(Number a, long b) 将溢出");
                }
            }
#endif

            return a.value != (b << BitOffset);
        }

        public static bool operator ==(long a, Number b)
        {
#if CHECK_NUMBER
            {
                if (a > RMaxRealValue || a < RMinRealValue)
                {
                    throw new Exception("Number public static bool operator ==(Number a, long b) 将溢出");
                }
            }
#endif

            return b.value == (a << BitOffset);
        }

        public static bool operator !=(long a, Number b)
        {
#if CHECK_NUMBER
            {
                if (a > RMaxRealValue || a < RMinRealValue)
                {
                    throw new Exception("Number public static bool operator !=(Number a, long b) 将溢出");
                }
            }
#endif

            return b.value != (a << BitOffset);
        }
        #endregion

        #region 常用方法
        /// <summary>
        /// 求平方根
        /// </summary>
        /// <returns></returns>
        public Number Sqrt()
        {
#if CHECK_NUMBER
            {
                if (this < Zero)
                {
                    throw new Exception("Number public Number Sqrt() 无法求负数的平方根");
                }
            }
#endif
            ulong v = (ulong)value;
            ulong num = 0uL;
            ulong num2 = 0uL;
            for (int i = 0; i < 32; i++)
            {
                num2 <<= 1;
                num <<= 2;
                num += v >> 62;
                v <<= 2;
                if (num2 < num)
                {
                    num2 += 1uL;
                    num -= num2;
                    num2 += 1uL;
                }
            }

            v = num2 >> 1 & 0xffffffffu;
            return v >> (BitOffset / 2);
        }

        /// <summary>
        /// 返回绝对值
        /// </summary>
        /// <returns></returns>
        public Number Abs()
        {
            return this > Zero ? this : -this;
        }

        /// <summary>
        /// 取值区间限定
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public Number Clamp(Number min, Number max)
        {
            if (this < min)
            {
                return this.value = min.value;
            }
            else if (this > max)
            {
                return this.value = max.value;
            }
            return this;
        }

        /// <summary>
        /// 是不是2的整数次方
        /// </summary>
        /// <returns></returns>
        public bool IsPowerOfTwo()
        {
            return this.value >= Number.N1 && (this.value & (this.value - 1)) == 0;
        }

        public int AsInt()
        {
            return (int)this;
        }

        public float AsFloat()
        {
            return (float)this;
        }
        #endregion
    }
}
